﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_RECT_H
#define _UXS_RECT_H

#pragma once

#include "TVec.h"
#include "TMatrix2.h"

namespace UX_STL
{
namespace UXMath
{

#	define RECT_OPERATOR( _op ) \
		\
		Self &	operator _op##= (const T& other)				{ left _op##= other;  bottom _op##= other;  right _op##= other;  top _op##= other;  return *this; } \
		Self	operator _op  (const T& other) const			{ return Self( left _op other, bottom _op other, right _op other, top _op other ); } \
		\
		Self &	operator _op##= (const Self& other)				{ left _op##= other.left;  bottom _op##= other.bottom;  right _op##= other.right;  top _op##= other.top;  return *this; } \
		Self	operator _op  (const Self& other) const			{ return Self( left _op other.left, bottom _op other.bottom, right _op other.right, top _op other.top ); } \
		\
		Self &	operator _op##= (const vec2_t& other)			{ left _op##= other.x;  bottom _op##= other.y;  right _op##= other.x;  top _op##= other.y;  return *this; } \
		Self	operator _op  (const vec2_t& other) const		{ return Self( left _op other.x, bottom _op other.y, right _op other.x, top _op other.y ); } \
		\
		friend Self & operator _op (const T& lvalue, const Self& rvalue)		{ return Self( lvalue, lvalue, lvalue, lvalue ) _op rvalue; } \
		friend Self & operator _op (const vec2_t& lvalue, const Self& rvalue)	{ return Self( lvalue.x, lvalue.y, lvalue.x, lvalue.y ) _op rvalue; } \
		

	//
	// Border
	//

	namespace e_border
	{
		enum	type
		{
			OUTER		= 0x00,
			INNER		= 0x11,		// in rect
			LEFT		= 0x02,
			RIGHT		= 0x04,
			TOP			= 0x20,
			BOTTOM		= 0x40,
			LB_ANGLE	= LEFT  | BOTTOM,
			LT_ANGLE	= LEFT  | TOP,
			RB_ANGLE	= RIGHT | BOTTOM,
			RT_ANGLE	= RIGHT | TOP
		};

		UX_ENUM_BIT_OPERATIONS( type )
	}



	//
	// Rect
	//

	template <typename T>
	struct TRect
	{
	// types
	public:
		typedef bool			_is_rect;
		typedef TRect<T>		Self;
		typedef T				value_t;
		typedef TVec<T,2>		vec2_t;
		typedef TVec<T,4>		vec4_t;
		typedef TLine< vec2_t >	line_t;

		enum EEdge : uint8
		{
			LEFT = 0,
			TOP,
			RIGHT,
			BOTTOM,
		};


	// variables
	public:
		T	left,
			bottom,
			right,
			top;
		

	public:
		// constructors
		TRect () :										left(0), bottom(0), right(0), top(0) {}
		TRect (T left, T bottom, T right, T top) :		left(left), bottom(bottom), right(right), top(top) {}
		TRect (const T *vect);
		TRect (const vec2_t &v0, const vec2_t &v1) :	left(v0.x), bottom(v0.y), right(v1.x), top(v1.y) {}
		TRect (const vec2_t &size) :					left(0), bottom(0), right(size.x), top(size.y) {}


		// unary operators
		Self		operator - () const;
		Self		operator ~ () const;
		Self		operator ! () const;


		// binary operators
		const T &	operator [] (usize i)	const	{ ASSUME(i<4);	return ptr()[i]; }
		T		&	operator [] (usize i)			{ ASSUME(i<4);	return ptr()[i]; }

		vec2_t		operator () (usize i)	const;

		bool		operator == (const Self &right) const;
		bool		operator != (const Self &right) const;

		RECT_OPERATOR( +  );
		RECT_OPERATOR( -  );
		RECT_OPERATOR( *  );
		RECT_OPERATOR( /  );
		RECT_OPERATOR( %  );
		RECT_OPERATOR( &  );
		RECT_OPERATOR( |  );
		RECT_OPERATOR( ^  );
		RECT_OPERATOR( >> );
		RECT_OPERATOR( << );
		

		// methods
		T		*	ptr ()							{ return PointerCast< T >( this ); }
		const T *	ptr () const					{ return PointerCast< T >( this ); }

		vec2_t		LeftBottom () const				{ return vec2_t( left, bottom ); }
		vec2_t &	LeftBottom ()					{ return *PointerCast< vec2_t >( &left ); }

		vec2_t		RightTop () const				{ return vec2_t( right, top ); }
		vec2_t &	RightTop ()						{ return *PointerCast< vec2_t >( &right ); }

		vec2_t		LeftTop () const				{ return vec2_t( left, top ); }
		vec2_t		RightBottom () const			{ return vec2_t( right, bottom ); }

		vec4_t		ToVec4 () const					{ return vec4_t( left, bottom, right, top ); }

		line_t		Left () const;
		line_t		Right () const;
		line_t		Bottom () const;
		line_t		Top () const;
		line_t		ToLine (EEdge e) const;

		T			Min () const;
		T			Max () const;

		T			Width () const					{ return right - left; }
		T			Height () const					{ return top - bottom; }

		Self &		SetCenterSize (const vec2_t &vCenter, const vec2_t &vSize);
		Self &		SetSize (const vec2_t &vSize);
		Self &		Scale (const vec2_t &sScale);
		Self &		Join (const Self &sRect);
		Self &		Stretch (const vec2_t &sValue);
		Self &		Stretch (T value)				{ return Stretch( vec2_t( value ) ); }

		vec2_t		GetSize ()		const;
		vec2_t		GetSum ()		const;
		vec2_t		GetCenter ()	const;

		// IsInner*
		bool  IsInnerLine (const line_t &sLine) const;
		bool  IsInnerPoint (const vec2_t &sPoint) const;
		bool  IsInnerRect (const Self &sRect) const;

		// Intersect*
		bool  IntersectLine (const line_t &sLine) const;
		bool  IntersectRect (const Self &sRect) const;

		// GetIntersection*
		bool  GetLineIntersection (const line_t &sLine, OUT vec2_t &result0, OUT vec2_t &result1) const;

		bool  CropRect (Self &sRect) const;
		bool  CropLine (line_t &sLine) const;

		bool  Sub (const Self &sRect, OUT Self &result0, OUT Self &result1) const;

		void  Rotate (T angleDeg, OUT vec2_t &p0, vec2_t &p1, vec2_t &p2, vec2_t &p3) const;
		void  RotateRad (T angleRad, OUT vec2_t &p0, vec2_t &p1, vec2_t &p2, vec2_t &p3) const;
		void  RotateSinCos (const vec2_t &vSinCos, OUT vec2_t &p0, vec2_t &p1, vec2_t &p2, vec2_t &p3) const;

		void  FromPoints (const vec2_t &p0, const vec2_t &p1);
		void  FromPoints (const vec2_t &p0, const vec2_t &p1, const vec2_t &p2, const vec2_t &p3);

		e_border::type PointInBorder (const vec2_t &sPoint, T tBorder) const;
		
		template <typename T2>
		TRect<T2>	Convert () const;

		template <typename B>
		const B		To () const;

		string ToString () const;
	};

	
	
	
//--------------------------- Math Func ---------------------------//

	template <typename T>
	inline TRect<T> Abs (const TRect<T> &val)
	{
		return TRect<T>( Abs(val.left), Abs(val.bottom), Abs(val.right), Abs(val.top) );
	}

	
	template <typename T>
	inline TRect<T> DTR (const TRect<T> &val)
	{
		return TRect<T>( DTR(val.left), DTR(val.bottom), DTR(val.right), DTR(val.top) );
	}

	
	template <typename T>
	inline TRect<T> RTD (const TRect<T> &val)
	{
		return TRect<T>( RTD(val.left), RTD(val.bottom), RTD(val.right), RTD(val.top) );
	}
	
	
	template <typename T>
	inline bool IsZero (const TRect<T>& val)
	{
		return IsZero(val.left) and IsZero(val.bottom) and IsZero(val.right) and IsZero(val.top);
	}

	
	template <typename T>
	inline TRect<T> SetSign (const TRect<T> &val, bool bSign)
	{
		return TRect<T>(	SetSign( val.left,  bSign ),	SetSign( val.bottom, bSign ),
							SetSign( val.right, bSign ),	SetSign( val.top,	 bSign ) );
	}
	
	
	template <typename T>
	inline TRect<T> Mod (const TRect<T>& left, const TRect<T>& right)
	{
		return TRect<T>(	Mod( left.left,  right.left ),	Mod( left.bottom, right.bottom ),
							Mod( left.right, right.right ), Mod( left.top,	  right.top ) );
	}

	
	template <typename R, typename T>
	inline R Round (const TRect<T>& val)
	{
		return R(	Round<R::value_t>(val.left),	Round<R::value_t>(val.bottom),
					Round<R::value_t>(val.right),	Round<R::value_t>(val.top) );
	}

	
	template <typename T>
	inline TRect<T> SafeDiv (const TRect<T>& left, const TRect<T>& right, const TRect<T>& defVal)
	{
		return TRect<T>(	SafeDiv( left.left,		right.left,		defVal.left ),
							SafeDiv( left.bottom,	right.bottom,	defVal.bottom ),
							SafeDiv( left.right,	right.right,	defVal.right ),
							SafeDiv( left.top,		right.top,		defVal.top ) );
	}



//--------------------------- TRect -------------------------------//

	template <typename T>
	inline TRect<T>::TRect (const T *vect): left(0), bottom(0), right(0), top(0)
	{
		ASSUME( vect != null );
		left	= vect[0];
		bottom	= vect[1];
		right	= vect[2];
		top		= vect[3];
	}
	

	template <typename T>
	inline T TRect<T>::Min () const
	{
		return UXMath::Min( left, bottom, right, top );
	}

	
	template <typename T>
	inline T TRect<T>::Max () const
	{
		return UXMath::Max( left, bottom, right, top );
	}

	
	template <typename T>
	inline bool TRect<T>::operator == (const Self &right) const
	{
		return ( (left  == right.left)  & (bottom == right.bottom) &
				 (right == right.right) & (top    == right.top) );
	}

	
	template <typename T>
	inline bool TRect<T>::operator != (const Self &right) const
	{
		return not (*this == right);
	}
		

	template <typename T>
	inline TVec<T,2> TRect<T>::operator () (usize i) const
	{
		ASSUME( i < 4 );
		return vec2_t( (*this)[ i & 2 ], (*this)[ ((( (i & 1) + (i >> 1) ) & 1) << 1) + 1 ] );
	}

	
	template <typename T>
	inline TVec<T,2> TRect<T>::GetSum () const
	{
		return vec2_t( left + right, bottom + top );
	}
	
	
	template <typename T>
	template <typename T2>
	inline TRect<T2> TRect<T>::Convert () const
	{
		return TRect<T2>( T2(left), T2(bottom), T2(right), T2(top) );
	}

	
	template <typename T>
	inline bool TRect<T>::IsInnerPoint (const vec2_t &sPoint) const
	{
		return ( (sPoint.x >= left)	  & (sPoint.x <= right) &
				 (sPoint.y >= bottom) & (sPoint.y <= top) );
	}
		

	template <typename T>
	inline bool TRect<T>::IsInnerRect (const Self &sRect) const
	{
		// this rect in other rect
		return ( (left >= sRect.left) & (right <= sRect.right) & (bottom >= sRect.bottom) & (top <= sRect.top) );
	}

	
	template <typename T>
	inline bool TRect<T>::IntersectRect (const Self &sRect) const
	{
		return !! ( (left < sRect.right) & (right > sRect.left) & (bottom < sRect.top) & (top > sRect.bottom) ) |
				  ( (sRect.right < left) & (sRect.left > right) & (sRect.top < bottom) & (sRect.bottom > top) );
	}
	

	template <typename T>
	inline TRect<T> & TRect<T>::SetCenterSize (const vec2_t &vCenter, const vec2_t &vSize)
	{
		const vec2_t	half_size = vSize / T(2);

		left	= vCenter.x - half_size.x;
		bottom	= vCenter.y - half_size.y;
		right	= vCenter.x + half_size.x;
		top		= vCenter.y + half_size.y;

		return *this;
	}


	template <typename T>
	inline TRect<T> & TRect<T>::SetSize (const vec2_t &vSize)
	{
		right	= left + vSize.x;
		top		= bottom + vSize.y;
		return *this;
	}
	

	template <typename T>
	inline TRect<T> & TRect<T>::Join (const Self &sRect)
	{
		left	= UXMath::Min( left,	sRect.left );
		bottom	= UXMath::Min( bottom,	sRect.bottom );
		right	= UXMath::Max( right,	sRect.right );
		top		= UXMath::Max( top,		sRect.top );
		return *this;
	}
	

	template <typename T>
	inline TRect<T> & TRect<T>::Stretch (const vec2_t &sValue)
	{
		LeftBottom() -= sValue;
		RightTop()   += sValue;
		return *this;
	}


	template <typename T>
	inline TVec<T,2> TRect<T>::GetCenter () const
	{
		return vec2_t( (right + left)/2, (top + bottom)/2 );
	}

		
	template <typename T>
	inline bool TRect<T>::CropRect (Self &sRect) const
	{
		// crop input rectangle
		//	 ----=====--
		//	|	|#####| | <-this
		//	|	|#####| |
		//	|	|#####| |
		//	 ----=====\-
		//   ^-sRect   ` result

		if ( IntersectRect( sRect ) )
		{
			if ( sRect.left   < left   )	sRect.left   = left;
			if ( sRect.bottom < bottom )	sRect.bottom = bottom;
			if ( sRect.right  > right  )	sRect.right  = right;
			if ( sRect.top    > top    )	sRect.top    = top;
			return true;
		}
		return false;
	}

	
	template <typename T>
	inline bool TRect<T>::Sub (const Self &sRect, OUT Self &result0, OUT Self &result1) const
	{
		// this = this - sRect
		//			 -----===-
		//			|####|   || <- sRect
		//	this ->	|####|   ||
		//			|####|   ||
		//			/-----===-
		//		result

		TODO("Rect::Sub()");
		return false;
	}

	
	template <typename T>
	inline e_border::type TRect<T>::PointInBorder (const vec2_t &sPoint, const T tBorder) const
	{
		e_border::type	ret = e_border::OUTER;

		if ( IsInnerPoint( sPoint ) )
		{
			ret  = e_border::INNER;
			ret |= ( (sPoint.x >= left)		& (sPoint.x <= left   + tBorder) ) ? e_border::LEFT		: 0;	// left
			ret |= ( (sPoint.y >= bottom)	& (sPoint.y <= bottom + tBorder) ) ? e_border::BOTTOM	: 0;	// bottom
			ret |= ( (sPoint.x <= right)	& (sPoint.x >= right  - tBorder) ) ? e_border::RIGHT	: 0;	// right
			ret |= ( (sPoint.y <= top)		& (sPoint.y >= top    - tBorder) ) ? e_border::TOP		: 0;	// top
		}
		return ret;
	}
	
	
	template <typename T>
	inline TRect<T> & TRect<T>::Scale(const vec2_t &sScale)
	{
		const T	cx	= (right + left) / (T)2,
				cy	= (top + bottom) / (T)2;
		const T	cw	= (right - left) * sScale.x / (T)2,
				ch	= (top - bottom) * sScale.y / (T)2;

		left	= cx - cw;
		bottom	= cy - ch;
		right	= cx + cw;
		top		= cy + ch;

		return *this;
	}
	
	
	template <typename T>
	inline TVec<T,2> TRect<T>::GetSize() const
	{
		return vec2_t( right - left, top - bottom );
	}

		
	template <typename T>
	inline void  TRect<T>::Rotate (T angleDeg, OUT vec2_t &p0, vec2_t &p1, vec2_t &p2, vec2_t &p3) const
	{
		return RotateRad( DTR( angleDeg ), p0, p1, p2, p3 );
	}
		

	template <typename T>
	inline void  TRect<T>::RotateRad (T angleRad, OUT vec2_t &p0, vec2_t &p1, vec2_t &p2, vec2_t &p3) const
	{
		return RotateSinCos( SinCos( angleRad ), p0, p1, p2, p3 );
	}
	

	template <typename T>
	inline void  TRect<T>::RotateSinCos (const vec2_t &sc, OUT vec2_t &p0, vec2_t &p1, vec2_t &p2, vec2_t &p3) const
	{
		const TMatrix<T,2,2>	m	= TMatrix<T,2,2>( sc[1], -sc[0], sc[0], sc[1] );

		p0 = m * (*this)(0);
		p1 = m * (*this)(1);
		p2 = m * (*this)(2);
		p3 = m * (*this)(3);
	}
	

	template <typename T>
	inline void  TRect<T>::FromPoints (const vec2_t &p0, const vec2_t &p1)
	{
		MinMax( left,   right, p0.x, p1.x );
		MinMax( bottom, top,   p0.y, p1.y );
	}
	

	template <typename T>
	inline void  TRect<T>::FromPoints (const vec2_t &p0, const vec2_t &p1, const vec2_t &p2, const vec2_t &p3)
	{
		left   = Min( p0.x, p1.x, p2.x, p3.x );
		right  = Max( p0.x, p1.x, p2.x, p3.x );
		bottom = Min( p0.y, p1.y, p2.y, p3.y );
		top    = Max( p0.y, p1.y, p2.y, p3.y );
	}


	template <typename T>
	template <typename B>
	inline const B  TRect<T>::To() const
	{
		STATIC_ASSERT( typename B::_is_rect(true), "type is not rect" );

		return Convert< typename B::value_t >();
	}

	
	template <typename T>
	inline string TRect<T>::ToString() const
	{
		return string("left: ") << left << ", bottom: " << bottom << ", right: " << right << ", top: " << top;
	}


#	undef	RECT_OPERATOR

//-------------------------------------------------------------------

}	// UXMath
}	// UX_STL

#endif	// _UXS_RECT_H